Skip to content

CTO Sub-Audit Fix Wave + moderation (#693) — money/security/booking integrity, Sonar-clean - #995

Merged
teetangh merged 63 commits into
devfrom
integration/cto-audit-wave
Jul 16, 2026
Merged

CTO Sub-Audit Fix Wave + moderation (#693) — money/security/booking integrity, Sonar-clean#995
teetangh merged 63 commits into
devfrom
integration/cto-audit-wave

Conversation

@teetangh

@teetangh teetangh commented Jul 11, 2026

Copy link
Copy Markdown
Contributor

CTO Sub-Audit Fix Wave — consolidated integration PR

Reviewer's note: This single PR integrates 14 separately-authored fix branches (originally PRs #981#994) into one review surface. Each area is one merge commit, so you can review commit-by-commit or file-by-file. The individual PRs remain open as superseded drafts for granular per-concern diffs — do not merge them; this PR is the one that goes to dev.

Where this came from (context from scratch)

A branch bugs/cto-subsystem-audit (PR #976) contained an LLM-generated architecture audit (Grok 4.5): ~250 claims of gaps/bugs across finances, booking, enterprise, stream, and compliance. We treated it as untrusted input and verified every claim against the actual codebase with parallel exploration agents before writing a single fix. That triage mattered — several of the audit's loudest claims were wrong:

The ~30 claims that survived verification became the fixes below. The audit pack itself was annotated in-place with per-claim verdicts (that lives in #976, kept separate from this PR to avoid bloating the diff with 3,900 lines of audit prose).

What's in this PR (14 fixes, grouped)

Money & ledger integrity (the core of the wave)

Booking correctness

Security & identity

Platform & cleanup

Review-bot triage already applied

Every gemini-code-assist / CodeRabbit thread on the constituent PRs was triaged and resolved before this consolidation. Review caught several genuine bugs the offline agents couldn't compile-check — including a appointmentId: undefined Prisma filter that could have cancelled every scheduled slot, a support-ticket PII leak, findUnique misused with relation filters, and the DM-channel privilege escalation — all fixed. Two bad bot suggestions were correctly rejected (a false "reconcile key mismatch" and an over-correcting reschedule-filter revert).

Schema & DB-push status (read before merging)

Three additive schema changes coexist here: the ConsultantReview unique pair (#987), EarningStatus.BATCHED (#993), and the PaymentGateway enum edit (#984).

  • The review-unique is safe — verified 0 duplicate pairs on the dev DB.
  • BATCHED is a safe additive enum value.
  • The gateway enum removal of LEMON_SQUEEZY/XFLOW is deferred to the pre-MVP DB reset — 156 Payment + 21 Refund + 10 Dispute rows still reference those values, so a db push that drops them would fail. The schema is correct for post-reset; per the no-backfill policy we don't migrate money records.

How to review this efficiently

  • By concern: each of the 14 areas is a single merge commit — git log --merges gives you the map.
  • Highest-risk files: lib/payments/webhooks/handlers.ts, lib/payments/operations/checkout.ts, lib/payments/payouts/earnings-service.ts, jobs/reconcile/reconcile-ledgers.ts.
  • Not compiled locally — the offline agents couldn't run tsc (the dev box OOMs on cold type-checks), so CI is the type-check + test gate. CI on the constituent branches passed (e.g. fix(payments): auto-refund captured-but-blocked funds + wallet-drift freeze #990 fully green); this integration branch re-runs everything.

Documentation reconciled (31 files)

This PR also updates the docs that these fixes made stale — payout/earnings lifecycle (adds BATCHED, PAID = COMPLETED+UTR), enterprise money-and-ledger and the PENDING_TRUST ADR (rescope to sponsoring org), the session-generation ADR (corrects the false "revokeSession kill switch" to the generation-bump reality), Stream user-management/token docs (least-privilege roles, session-bound minting), recording-webhook docs (immediate enqueue via after()), booking rescheduling/waitlist/cron docs, gateway docs (Lemon/XFlow removed), and the compliance company-name references (→ "Practitionist"). Docs were written to match the code, so a couple of my brief's phrasings were corrected in the process (e.g. only manual allocation shards by day; autoAllocate stays consultant-wide).

Two things worth a product/reviewer check (not blockers)

  1. Partial-reschedule surfacing. fix(booking): correctness sweep — #448 status scope, #837 allocation idempotency, #860 lock sharding #988 correctly stops a single-session reschedule from flipping the whole subscription to PENDING — but the consultant "Requests" tab query still filters status = PENDING, so a partial reschedule may no longer surface to the consultant. Worth confirming the intended UX.
  2. Checkout 409 on wallet-freeze. The /api/checkout route classifies errors by message pattern, not by the httpStatus we added to WalletFrozenError, so a frozen-wallet checkout won't return a clean 409 from that specific endpoint unless we add a "frozen" pattern. Minor, out of scope for this wave.

Also folded into this branch (post-consolidation)

  • Moderation subsystem (Moderation + profile verification subsystem audit #693 / PR feat(moderation): real side-effects for staff actions (#693) + Stream authz guards + task-folder retirement #974) reconciled in. The moderation-actions branch is merged here with union resolutions (not take-one-side): assertCanMintToken keeps both the cache-bypass (getSession(true)) and the banned-user check; the review rating helper is now soft-delete-aware (deletedAt: null) so moderation-removed reviews don't count; channel.action.ts carries both the channel-scoped moderator grant and the addMemberToChannel authz gate; and the BetterAuth admin plugin now defines proper roles (ADMIN/STAFF) so the app builds. The Stream role model was verified empirically via client-scoped capability checks (a consultant moderates only their own channels; STAFF/ADMIN are app-wide; consultees none).

  • SonarCloud quality gate: green. Fixed the one reliability bug it caught (a toSorted() missing a localeCompare comparator — S2871), then reduced cognitive complexity below threshold on 9 functions (SSO PATCH, onboarding submit, recording cleanup, no-show detector, and the moderation orchestrators side-effects / action-route / cancel-user-engagements) via pure helper extraction — behavior-preserving, each confirmed 0-complexity by the Sonar analyzer, tsc + eslint clean, full jest suite green. Review-bot threads (gemini + CodeRabbit) across the branch are all triaged and resolved.

Issues closed on merge

Closes #860 · Closes #891 · Closes #471
Part of #837 · #899 · #687 · #724 · #840 · #448 · #693

Summary by CodeRabbit

  • New Features
    • Added idempotent booking allocation to prevent duplicate bookings from repeated submissions.
    • Added “Processing payout” (BATCHED) earnings status and updated payout/earnings tracking.
    • Added collaborator permission flags and improved attendee roster access controls.
    • Added automated consultant no-show detection with automatic refunds and notifications.
    • Added least-privilege Stream channel moderation and strengthened token authorization.
  • Bug Fixes
    • Improved waitlist capacity handling, review consistency, subscription rescheduling behavior, and support-ticket deduplication.
    • Refined public booking roster access and improved concurrency safety for review changes.
  • Changes
    • Payment support is now focused on Stripe, Razorpay, and card payments.
    • Updated public pages’ company/contact information (removed address blocks).

teetangh and others added 30 commits July 11, 2026 13:21
…s (Part of #693)

BetterAuth admin plugin (banned/banReason/banExpires, defaultRole
CONSULTEE) + two-phase orchestrator in lib/moderation: transactional
ban flags, session revocation, earnings hold, profile unverification,
review soft-delete; best-effort bulk cancel with 100% refunds, Stream
revoke/deactivate, Novu notifies — outcome persisted to
ModerationAction.sideEffects. Fixes the dashboard actionType contract
mismatch (every staff action 400'd) and adds suspension duration
presets. Starts #725 Tier-1 (admin plugin adoption).

Schema (pending coordinated db push): User ban fields,
Session.impersonatedBy, ConsultantReview.deletedAt (+ public-read
filters), ModerationAction.sideEffects, CancellationReason.MODERATION.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…Part of #899)

Stream's server-side API bypasses its permission system, so the gate
must live app-side: token minting now requires a session, is bound to
the caller's own userId (staff/admin excepted), and refuses banned
users — a revoked token was otherwise trivially re-mintable.
addMemberToChannel (previously unguarded, zero callers) now allows
only staff/admin or the channel creator, without lazy channel creation
for non-privileged callers.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ects (Part of #734)

The payment-success and payment-failure notification paths dragged
4–5-level includes (full User + profile rows across all four
appointment shapes) to read an id and a name. OPT-1 from the retired
payment task file.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…-chat decisions

All 13 task files verified superseded — fixed in code, tracked in open
issues (#738/#899/#701/#863), or drafts of issue bodies that exist.
The 2026-07-10 issue triage plan moves into docs/roadmap so it is
tracked. Stream docs now state the deliberate consultee↔consultee DM
block (group/event channels are the sanctioned shared space), the
deterministic channel-id design, and moderation token
revocation/deactivation. New ADR covers the #693 enforcement design.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…fe Set iteration, number money fields

Part of #693.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…merged 404 branch, session-mocked stream tests

Gemini triage on PR #974: suspension-days input NaN sentinel so the
field can be cleared (Suspend disables until valid); single 404
conditional in the review GET. The channel-actions suite mocks
auth-server/auth-helpers (jest can't parse better-auth ESM) and gains
3 tests for the #899 addMemberToChannel guard.

Part of #693, #899.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…e server errors in toast, roadmap addendum

The non-throwing Novu wrappers return {success:false} on delivery
failure, so sideEffects.notification now reads the flag instead of
always "ok". The staff toast surfaces the API's 400/409 message.
Roadmap snapshot gains a dated #693-implemented addendum; the ADR
names the best-effort reconciliation path as an explicit follow-up
(the 409 guard defers it, thread left open).

Part of #693.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Any caller could mint any user's Stream identity and every user was a global Stream admin.

Part of #899
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ent at checkout

#971 shipped ProgramConsultantAllowlist and Membership.exclusiveEngagement
as write-only stubs: the flag's only reference was a default-false write and
the allowlist was never read at checkout. Enforcement now runs inside
revalidateInsideLock, under the distributed lock, exactly where the ADR-18
comment pointed: allowlist rows on the funding Program restrict org-sponsored
bookings to listed consultants (zero rows = open network), and an ACTIVE
membership with exclusiveEngagement blocks the consultant's independent
(non-org-owned) plans. Defaults are unchanged, so behaviour only shifts when
an operator opts in. The "hide" half of exclusivity (marketplace visibility
filtering) remains future work per ADR 18.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…per pair, recompute rating on all mutations

POST /api/user/reviews trusted the body's consulteeProfileId, letting any
user post reviews as any consultee, with no check that they ever booked the
consultant, no per-pair uniqueness, and no recompute of the denormalized
ConsultantProfile.rating that explore sort/filter reads. Reviews are now
authored only as the session's own consultee profile, gated on a completed
booking (COMPLETED consultation/subscription, a held slot, or a
COMPLETED/CONVERTED trial), deduped by @@unique on
(consultantProfileId, consulteeProfileId) with P2002 mapped to 409, and
every create/update/delete recomputes the rating in-transaction via a
shared lib/reviews helper. Also drops the email OR-clause from the public
consultant search, which was a PII enumeration key.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…hannels

Extends least-privilege role mapping: video-call host and event-channel
creators get channel-scoped grants instead of relying on global admin.

Part of #899
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…loads, delete objects on retention

Phase-0 hardening: wire the dead queueRecordingTransfer so SUPABASE_PERMANENT
recordings enqueue on recording_ready (cron becomes backstop); stream uploads
instead of buffering the whole blob; retention now deletes the Supabase object,
not just the DB row; bounded transfer concurrency; <72h backlog alert; doc drift.

Part of #899
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ENTS gateway enum

Gateway-evaluation ADR marks Lemon/XFlow REMOVE; deletes their checkout stubs,
webhook routes, config, seed refs and doc mentions (and stray day-pass docs).
Stripe retained. Adds DODO_PAYMENTS enum value for post-MVP evaluation.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…endly conflict

Reuses the checkout tentative-hold machinery so a NOTIFIED waitlist user gets a
real seat reservation for the response window instead of an FCFS race at
checkout; join is transactional with a friendly 409 on double-join.

Part of #837
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…dempotency, widen reconcile, tx CRUD, shard auto-allocate lock

A single/multi-session subscription reschedule no longer flips the whole
subscription to PENDING (#448); allocate endpoints dedupe double-submits via
Appointment.allocationIdempotencyKey (#837); the reconcile detector uses the
canonical occupancy filter so unpaid/tentative overlaps are caught; webinar/
class plan deletes wrap guard-check+delete in a Serializable tx to close the
check-then-act race; the auto-allocate Redis lock shards by target day so
non-overlapping-day allocations for one consultant no longer serialize (#860),
backstopped by the #440 GiST exclusion constraint.

Closes #860
Part of #837, #448
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ollaborator perms, legal constants, DPDP docstring

Trust & safety sweep bundling seven correctness/compliance fixes:
- Referral stash no longer wiped when landing without ?ref= (#891).
- Novu triggers carry a deterministic transactionId (dedup) and fail loud
  via Sentry when unconfigured in prod; reminders pass a per-window key.
- Payment-linked support tickets dedup against an open ticket; staff status
  transition is now a status-guarded CAS (updateMany).
- Collaborator permission booleans are set at invite time; canSeeAttendees
  is enforced on the participant-roster endpoints.
- Legal constants set name=Practitionist and drop the [ADDRESS] placeholder
  and its rendered blocks; contact emails kept as loud TODO placeholders.
- DPDP header docstring corrected to describe the live fail-closed behavior.
- Webinar/class XOR added to the check-constraints.sql sidecar.

Closes #891
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…eeze wallet on ledger drift

Two verified webhook paths left captured funds stuck on manual ops, and a
detected wallet cache/journal drift was only reported. Reuse the existing
refundPayment infra to auto-refund both captured-but-blocked cases (manual
recovery preserved as the fallback if the refund call itself throws), and
introduce a scoped wallet-spend freeze the checkout path honors on drift.

Part of #837
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…s, correct session-revoke claim on membership removal

Kills last-write-wins races: compare-and-swap on onboarding final submit
(guarded on onboardingCompleted) and SSO settings PATCH (optimistic
expectedVersion lock); SCIM user provisioning now honors the unverified-org
seat governance (UNVERIFIED_ORG_SEAT_CAP) the invite path already enforces.

For membership removal, no true server-side revoke-by-userId exists: the
BetterAuth admin plugin (revokeUserSessions) is not installed and core
revokeSession needs the target's own session token, so the misleading
"calls revokeSession" comment is corrected to describe the real mechanism —
the sessionGeneration bump the customSession reader honors, with the 24h
cookie-cache window as backstop.

Part of #724, #840
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…t earnings, wire domain-verify gate

The INVOICE-sponsor trust park was mis-wired: it keyed on the expert's HOST
org instead of the sponsoring org that owes the invoice, consultant earnings
were never parked at all, and the domain-verification gate had zero callers.
An unverified sponsor could book, accrue real consultant/org payables, and
ghost the invoice (#687 threat model).

- E-01: key the PENDING_TRUST decision on payment.organizationId (the sponsor),
  decided once and applied to consultant, primary-org and collaborator-org rows.
- E-02: park ConsultantEarnings under the same sponsor gate; extend the
  release-valve cron to promote parked consultant rows (joined via Payment).
- K-02: call assertVerifiedDomainOrThrow on the INVOICE checkout accrual path
  and the fundingSource->INVOICE transition.
- Host-dashboard honesty: gate host-earnings query/route on ENABLE_HOST_ORGS.

Part of #687, #837
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…fication

The platform promised a full refund on a consultant no-show but had no code
path — only the MeetingAttendance foundation data existed. Add an hourly job
that detects confirmed consultant no-shows (consultee joined, consultant never
did, past a conservative grace window), full-refunds via B1's refundPayment,
cancels the booking, and notifies both parties. Idempotent via a status CAS
claim plus refundPayment's refundable-balance guard. No schema changes.

Closes #471
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Earnings flipped to PAID the moment a payout BATCH was created — before
any gateway wire / UTR, and even with ENABLE_LIVE_PAYOUTS off. Finance
exports and consultant/host dashboards therefore claimed money had moved
when no cash had left. Introduce a BATCHED status: batch creation stages
earnings as BATCHED; PAID is written only when the payout row reaches
COMPLETED (+ UTR). Batch-eligibility excludes BATCHED, and every failure
/ reject / reversal path that predates COMPLETED releases BATCHED back to
READY.

Part of #837
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… window

Option B (compensating-write, not tx-fold). Folding earnings + the BOOKING
journal into the checkout/webhook tx is unsafe: createEarningsFromPayment opens
its OWN withSerializableRetry(prisma.$transaction(Serializable)) — the #896
waiver-race guard needs to abort + re-run the whole tx, impossible nested — and
the double-entry journal lives inside that same internal tx, so folding would
balloon the outer tx's lock footprint/duration. So we keep it post-commit but
stop pretending success: on failure we now page (recordSystemError → Sentry
ERROR + durable SystemEvent) instead of a silent level:warning. The guaranteed
idempotent healer is the existing data-state sync-payment-earnings scan
(SUCCEEDED payment + earnings:none), which keys on row state, not on this
marker. B1 auto-refund / B2 PENDING_TRUST park / B3 BATCHED status are untouched
— initialEarningStatus logic stays inside createEarningsFromPayment.

Part of #837
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…BigInt-safe amount compare

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…response, findFirst for relation filters

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ual-recovery marker after auto-refund

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…tentative holds

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…compute

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…-message channels

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…etention delete concurrency

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
teetangh and others added 2 commits July 12, 2026 02:18
…e wallet-freeze 409, re-check allocation idempotency, eslint build-fixes

Part of #995
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ave behavior changes

- reschedule: partial no longer flips subscription; mock subscription.count
- allocation: day-sharded manual lock key
- capture parity: auto-refund path
- payouts: BATCHED status transitions

Part of #995
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
schemas/checkout.ts (1)

14-24: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Do not advertise CARD as supported without routing it explicitly.

SupportedCheckoutGateway accepts "CARD", but routeGateway only honors an explicit "STRIPE" request; "CARD" falls through to the Razorpay domestic/IBT branches. A valid checkout request can therefore use a different provider than requested. Remove "CARD" until implemented, or add an explicit mapping and tests for domestic and international buyers.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@schemas/checkout.ts` around lines 14 - 24, Remove "CARD" from
paymentGatewaySchema and ensure SupportedCheckoutGateway no longer advertises it
until routeGateway explicitly supports that provider. Preserve the existing
STRIPE and RAZORPAY gateway behavior.
utils/slotAllocation/SlotAllocationService.ts (1)

1753-1757: 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Keep the existing idempotency key on reused 1:1 appointments. Replacing allocationIdempotencyKey on the preserved row drops the old replay marker, so a late retry of the earlier allocation can miss the dedupe guard and allocate again. Store replay keys separately or retain prior keys on the appointment.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@utils/slotAllocation/SlotAllocationService.ts` around lines 1753 - 1757,
Update the reused-appointment branch in SlotAllocationService so the existing
allocationIdempotencyKey on the preserved 1:1 appointment is not overwritten by
idempotencyData. Retain the prior replay key while storing any new replay keys
separately, ensuring late retries of earlier allocations still hit the
deduplication guard.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@schemas/checkout.ts`:
- Around line 14-24: Remove "CARD" from paymentGatewaySchema and ensure
SupportedCheckoutGateway no longer advertises it until routeGateway explicitly
supports that provider. Preserve the existing STRIPE and RAZORPAY gateway
behavior.

In `@utils/slotAllocation/SlotAllocationService.ts`:
- Around line 1753-1757: Update the reused-appointment branch in
SlotAllocationService so the existing allocationIdempotencyKey on the preserved
1:1 appointment is not overwritten by idempotencyData. Retain the prior replay
key while storing any new replay keys separately, ensuring late retries of
earlier allocations still hit the deduplication guard.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: b0a9b51f-8d01-491d-9f51-17fffc64a7ff

📥 Commits

Reviewing files that changed from the base of the PR and between d4a6e68 and 6accddb.

📒 Files selected for processing (27)
  • __tests__/booking-algorithm/rescheduleCancel.test.ts
  • __tests__/booking-algorithm/rescheduleResponses.test.ts
  • __tests__/booking-algorithm/slotAllocationService.test.ts
  • __tests__/enterprise/live-payout-submission.test.ts
  • __tests__/payments/capture-amount-parity.test.ts
  • __tests__/payments/stuck-payouts-money-handler.test.ts
  • actions/stream/meetings/meeting.action.ts
  • app/api/checkout/route.ts
  • app/api/organizations/[orgId]/sso/route.ts
  • app/checkout/plans/class/[planId]/page.tsx
  • app/checkout/plans/consultation/[planId]/page.tsx
  • app/checkout/plans/subscription/[planId]/page.tsx
  • app/checkout/plans/webinar/[planId]/page.tsx
  • app/dashboard/consultant/[consultantId]/(features)/earnings/page.tsx
  • docs/compliance/10-rbi-pa-and-payment-architecture.md
  • jobs/reconcile/reconcile-ledgers.ts
  • lib/data/org-analytics.ts
  • lib/novu/service.ts
  • lib/payments/gateway-router.ts
  • lib/payments/operations/checkout.ts
  • lib/payments/payouts/earnings-service.ts
  • lib/stream/recording-transfer-service.ts
  • lib/waitlist/slot-handler.ts
  • schemas/checkout.ts
  • scripts/cleanup/cleanup-old-stream-recordings.ts
  • tests/typescript/race-conditions/test-checkout-race-condition-fix.ts
  • utils/slotAllocation/SlotAllocationService.ts
💤 Files with no reviewable changes (1)
  • actions/stream/meetings/meeting.action.ts

teetangh and others added 15 commits July 16, 2026 19:41
… onto dev

Part of #693
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…gin initializes at build

Part of #693
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The real build fix is the BetterAuth admin roles config (888a7ae); the
analytics route builds fine without force-dynamic, same as on dev.

Part of #693
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Merges feature/moderation-actions-693 (#974) into the integration branch so
#995 merges cleanly after #974. Union resolutions (not take-one-side):

- stream.action.ts assertCanMintToken: keep #995's getSession(true) cache-bypass
  AND #974's banned-user check AND the isPrivileged/forUserId gate.
- lib/reviews.ts recomputeConsultantRating: add deletedAt:null so soft-removed
  reviews (#974) don't count — the DRY helper (#987) now honors soft-delete;
  staff-moderation route uses the helper instead of an inline duplicate.
- user/reviews/[id] select: union consulteeProfileId + consultantProfileId
  (#987 recompute) + deletedAt (#974 edit-guard).
- channel.action.ts auto-merged: retains both #995's channel_moderator grant
  and #974's addMemberToChannel authz gate.
- tasks/1.txt: honor #974 task-folder retirement (deleted).

Part of #995, #693
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…r.NaN

Review triage: doc now states re-run is blocked by the 409 guard (remediation
is manual via the best-effort-persisted sideEffects summary); NaN -> Number.NaN.

Part of #693
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The earlier prefer-array-to-sorted fix left toSorted() without a compare
function, which Sonar flags as an unreliable string sort (new_reliability_rating
D). Add localeCompare — matches the payload sort above and makes the dedup key
deterministic.

Part of #995
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Part of #693
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Part of #995
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ating

Number.parseInt("7.5") silently became 7; validate as an integer and fall back
to the NaN sentinel (Suspend stays disabled) for non-integers. (CodeRabbit)

Part of #693
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…p/no-show fns (S3776)

Helper extraction only — no behavior change. Money/moderation orchestrators
were accepted in Sonar instead.

Part of #995
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…into integration/cto-audit-wave

# Conflicts:
#	app/dashboard/staff/[staffId]/(features)/moderation/page.tsx
…ion/cancel orchestrators (S3776)

Helper extraction only — byte-identical behavior (phase split, CAS guards,
refund/hold/ban ordering, and per-step catch-and-record all preserved).

Part of #693
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@sonarqubecloud

Copy link
Copy Markdown

@teetangh teetangh changed the title CTO Sub-Audit Fix Wave — 14 verified fixes (money integrity, booking, security, platform) CTO Sub-Audit Fix Wave + moderation (#693) — money/security/booking integrity, Sonar-clean Jul 16, 2026
@teetangh
teetangh merged commit a637b8e into dev Jul 16, 2026
8 checks passed
@teetangh
teetangh deleted the integration/cto-audit-wave branch July 30, 2026 13:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant